package org.web3j.codegen;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.util.encoders.Hex;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.Sign;
import org.web3j.crypto.TransactionDecoder;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;
import org.web3j.protocol.ObjectMapperFactory;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.core.methods.response.Web3ClientVersion;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.ChainId;
import org.web3j.tx.gas.DefaultGasProvider;
import org.web3j.tx.gas.StaticGasProvider;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 冷钱包 账号 交易相关
 */
public class ColdWallet {

	private static Web3j web3j;

	private static String d = "coldwallet";

	private static String address = "0x4633092aa4b4cf048685387a9816f99453f20979";

	private static String keystore = "{\"address\":\"a530d89646db11abfa701e148e87324355fc6ea7\",\"id\":\"246e7d1d-8f31-4a3e-951d-41722213a44f\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"ciphertext\":\"26d10977bc199f6b678e89d3b7c3874bab3cddda18b92c014890d80657d7cc6a\",\"cipherparams\":{\"iv\":\"beaa9a404f793e86460a1fc71a0372a8\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"f06eb3d208db1643671c6e0210789f05e6de1746252fe5b83a38618e2bd18f1e\"},\"mac\":\"0aa4f85dfecaf8203ad0ee22c47ff6fb35b8f47d8f56ddb890ef2d513a06a801\"}}\n";

	private static String privateKey = "00be6b6223868e4ca31bf986fac76e9190fdcfa78bc432647e51cb43599628def7";

//	address 4633092aa4b4cf048685387a9816f99453f20979
//	00be6b6223868e4ca31bf986fac76e9190fdcfa78bc432647e51cb43599628def7
//	7c0df96c3ac6f30591b77b51e4366d495d90d39a45d47341d458cfa6e97778f3530be3af878981280b6d74d185ee8b62ed340510e6d4eb2bcf955d10c34775ce
//	keystore json file {"address":"4633092aa4b4cf048685387a9816f99453f20979","id":"b55681b6-5535-4062-83fe-b48e3e471bb9","version":3,"crypto":{"cipher":"aes-128-ctr","ciphertext":"14ce7e5accbfb769876c2a277111811646a8d1454dbdf504da0a7ac79b3288b5","cipherparams":{"iv":"0f4d5ec48bbb9c0ee7e0bef61a8d385a"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c733e51477c27438a5625774c05614d0270832c7e1566ac7672a4a38f1c2104e"},"mac":"01dc1f316eac958d3efdf2704b656490e578b552ad959a60fd6db0b748b2e459"}}

	public static void main(String[] args) {
		web3j = Web3j.build(new HttpService());
		try {
//			createWallet("123456");
//			decryptWallet(keystore, "11111111");
//			testTransaction();
//			testTokenTransaction();
			
			Web3ClientVersion web3ClientVersion = web3j.web3ClientVersion().send();
			String clientVersion = web3ClientVersion.getWeb3ClientVersion();
			System.out.println(clientVersion);
			
			EthGetBalance ethGetBalance = web3j.ethGetBalance("0x59e9f0c95fbf192f66609acccbe13c0719da9dbd", DefaultBlockParameterName.LATEST).send();
			BigInteger balance = ethGetBalance.getBalance();
			System.out.println(balance);
			
//			deploy();
//			use("0x74016b2375211134c8e57a9b2476eab9223b9879");
			//ide
//			use("0xc6f861efa250714d2037ba831d58863a8d201d84");
			//cmd
			use("0x5574046070176e0c40b3A7AC32045BD055F9AC7f");
			
			//3110315588457890234925478558883235304622266617566587134182814151979386768464
			//20617881364279886362329407427808919354848007217296223416362996074543214828638
			//00be6b6223868e4ca31bf986fac76e9190fdcfa78bc432647e51cb43599628def7
			
			String byteStr = "0x6e060511ce507d3ef7f26abab0e875ce9622612fca50ecd2b281696485f8c50";
			byte[] bytes = Numeric.hexStringToByteArray(byteStr);
			System.out.println(new BigInteger(bytes));
			
			//044d9bc31bc5378bf5441dcac30c33bed991816bd1e72607c3c7f3769ecf1d4485cc42bc936eee086373b8f588e5e60eb17e6bf0b16d02f941aec5d9bc2459d5e5
			
//			59936875151878128664233327856922375870732784481303488612109974194328462332677
//			59936875151878128664233327856922375870732784481303488612109974194328462332677
//			848311b3a9ce4f4a7788eac5f424404bbdf1f33fb29d18916e9e85aec6469305
//			0x344A41Ddf98eCB18A8a2980E748144cCd82ed635
//			0x344a41ddf98ecb18a8a2980e748144ccd82ed635
			byteStr = "848311b3a9ce4f4a7788eac5f424404bbdf1f33fb29d18916e9e85aec6469305";
			bytes = Numeric.hexStringToByteArray(byteStr);
			
			BigInteger priv = new BigInteger(1, bytes);
			BigInteger pub = Sign.publicKeyFromPrivate(priv);
			
			System.out.println(Keys.getAddress(pub));
			System.out.println(Hex.toHexString(Sign.getPublicKeyBytes(pub)));
			
			//0xfe60385467dc456212be0f17258c3a3d78717c2dc05eeeb555a8901e9b776d24
			//0x57842a65cc483910e5021d0dedd8d3230549395fdc79bfb9178ec37b839d9c4b
			//0x6e060511ce507d3ef7f26abab0e875ce9622612fca50ecd2b281696485f8c50
			
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// ["0x8f5be3496ce31d0e8eeaccea09875253a70064a8",
	// "0x9022c9d4777804e395fb4289566d71bd5c49b5cb"]
	
	
	private static void testTransaction() {
		BigInteger nonce;
		EthGetTransactionCount ethGetTransactionCount = null;
		//"0x59e9f0c95fbf192f66609acccbe13c0719da9dbd", "0x3d889ca41d8e7d6cfe9230fe3a820f98d97bd853"
		//String from = "0x59e9f0c95fbf192f66609acccbe13c0719da9dbd".toLowerCase();
		//String to = "0x3d889ca41d8e7d6cfe9230fe3a820f98d97bd853".toLowerCase();
		// ["0xdef6f56244a443132c23ca05c1dbb01a105cd131", "0xe77ce3e2ddb5bf795fe3526a761807494dcde21d"]
		String from = "0x25e63ae307A51c70493D29DDf145c2d7113fA943".toLowerCase();
		String to = "0xe77ce3e2ddb5bf795fe3526a761807494dcde21d".toLowerCase();
		address = from;
		try {
			ethGetTransactionCount = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).send();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (ethGetTransactionCount == null)
			return;
		nonce = ethGetTransactionCount.getTransactionCount();
		BigInteger gasPrice = Convert.toWei(BigDecimal.valueOf(3), Convert.Unit.GWEI).toBigInteger();
		BigInteger gasLimit = BigInteger.valueOf(30000);
		
		DefaultGasProvider gasProvider = new DefaultGasProvider();
//		String to = "0x9022c9d4777804e395fb4289566d71bd5c49b5cb".toLowerCase();
		BigInteger value = Convert.toWei(BigDecimal.valueOf(0.5), Convert.Unit.ETHER).toBigInteger();
		String data = "";
		byte chainId = ChainId.ROPSTEN;// 测试网络
		chainId = (byte) 1000;
		String privateKey = ColdWallet.privateKey;
		String signedData;
		try {
			signedData = signTransaction(nonce, //
					gasProvider.getGasPrice(), //
					gasProvider.getGasLimit(), //
					to, value, data, chainId, privateKey);
			if (signedData != null) {
				EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(signedData).send();
				System.out.println(ethSendTransaction.getTransactionHash());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static void testTokenTransaction(Web3j web3j, String fromAddress, String privateKey, String contractAddress,
			String toAddress, double amount, int decimals) {
		BigInteger nonce;
		EthGetTransactionCount ethGetTransactionCount = null;
		try {
			ethGetTransactionCount = web3j.ethGetTransactionCount(fromAddress, DefaultBlockParameterName.PENDING)
					.send();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (ethGetTransactionCount == null)
			return;
		nonce = ethGetTransactionCount.getTransactionCount();
		System.out.println("nonce " + nonce);
		BigInteger gasPrice = Convert.toWei(BigDecimal.valueOf(1), Convert.Unit.GWEI).toBigInteger();
		BigInteger gasLimit = BigInteger.valueOf(60000);
		BigInteger value = BigInteger.ZERO;
		// token转账参数
		String methodName = "transfer";
		List<Type> inputParameters = new ArrayList<>();
		List<TypeReference<?>> outputParameters = new ArrayList<>();
		Address tAddress = new Address(toAddress);
		Uint256 tokenValue = new Uint256(
				BigDecimal.valueOf(amount).multiply(BigDecimal.TEN.pow(decimals)).toBigInteger());
		inputParameters.add(tAddress);
		inputParameters.add(tokenValue);
		TypeReference<Bool> typeReference = new TypeReference<Bool>() {
		};
		outputParameters.add(typeReference);
		Function function = new Function(methodName, inputParameters, outputParameters);
		String data = FunctionEncoder.encode(function);

		byte chainId = ChainId.NONE;
		String signedData;
		try {
			signedData = ColdWallet.signTransaction(nonce, gasPrice, gasLimit, contractAddress, value, data, chainId,
					privateKey);
			if (signedData != null) {
				EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(signedData).send();
				System.out.println(ethSendTransaction.getTransactionHash());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 创建钱包
	 *
	 * @param password 密码
	 */
	public static void createWallet(String password) throws InvalidAlgorithmParameterException,
			NoSuchAlgorithmException, NoSuchProviderException, CipherException, JsonProcessingException {
		WalletFile walletFile;
		ECKeyPair ecKeyPair = Keys.createEcsmKeyPair();
		walletFile = Wallet.createStandard(password, ecKeyPair);
		System.out.println("address " + walletFile.getAddress());
		System.out.println(Hex.toHexString(ecKeyPair.getPrivateKey().toByteArray()));
		System.out.println(Hex.toHexString(ecKeyPair.getPublicKey().toByteArray()));
		ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
		String jsonStr = objectMapper.writeValueAsString(walletFile);
		System.out.println("keystore json file " + jsonStr);
	}

	/**
	 * 解密keystore 得到私钥
	 *
	 * @param keystore
	 * @param password
	 */
	public static String decryptWallet(String keystore, String password) {
		String privateKey = null;
		ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
		try {
			WalletFile walletFile = objectMapper.readValue(keystore, WalletFile.class);
			ECKeyPair ecKeyPair = null;
			ecKeyPair = Wallet.decrypt(password, walletFile);
			privateKey = ecKeyPair.getPrivateKey().toString(16);
			System.out.println(privateKey);
		} catch (CipherException e) {
			if ("Invalid password provided".equals(e.getMessage())) {
				System.out.println("密码错误");
			}
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return privateKey;
	}

	/**
	 * 签名交易
	 */
	public static String signTransaction(BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to,
			BigInteger value, String data, byte chainId, String privateKey) throws IOException {
		byte[] signedMessage;
		RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, to, value, data);

		if (privateKey.startsWith("0x")) {
			privateKey = privateKey.substring(2);
		}
		System.out.println(privateKey);
		ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
		Credentials credentials = Credentials.create(ecKeyPair);

		if (chainId > ChainId.NONE) {
			signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
		} else {
			signedMessage = TransactionEncoder.signMessage(rawTransaction, 1000L, credentials);
		}

		String hexValue = Numeric.toHexString(signedMessage);
		System.err.println(hexValue);
		RawTransaction decode = TransactionDecoder.decode(hexValue);
		System.err.println(decode);
		System.err.println(rawTransaction);
		
		
		return hexValue;
	}
	
	private static void deploy() {
		if (privateKey.startsWith("0x")) {
			privateKey = privateKey.substring(2);
		}
		System.out.println(privateKey);
		ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
		Credentials credentials = Credentials.create(ecKeyPair);
//		Credentials credentials = null;//可以根据私钥生成
//		RemoteCall<Greeter> deploy = Greeter.deploy(web3j, credentials,
//				Convert.toWei("10", Convert.Unit.GWEI).toBigInteger(), BigInteger.valueOf(3000000),
//				BigInteger.valueOf(5201314), "my token", "mt");
		//Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, Utf8String _greeting
		BigInteger gasPrice = Convert.toWei("10", Convert.Unit.GWEI).toBigInteger();
		BigInteger gasLimit = BigInteger.valueOf(3000000);
		Utf8String _greeting = new Utf8String("adopapa");
		
		StaticGasProvider gasProvider = new DefaultGasProvider();
		
//		RemoteCall<Greeter> deploy = Greeter.deploy(web3j, credentials, gasPrice, gasLimit, _greeting);
		RemoteCall<Greeter> deploy = Greeter.deploy(web3j, credentials, gasProvider, _greeting);
		
		try {
			Greeter greeter = deploy.send();
			System.out.println(greeter.getContractAddress());
			
			use(greeter.getContractAddress());
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private static void use(String address) {
		if (privateKey.startsWith("0x")) {
			privateKey = privateKey.substring(2);
		}
		System.out.println(privateKey);
		ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16));
		Credentials credentials = Credentials.create(ecKeyPair);
		
		String contractAddress = address;
		StaticGasProvider gasProvider = new DefaultGasProvider();
		Greeter contract = Greeter.load(contractAddress, web3j, credentials, gasProvider);
		String myAddress = null;
		String toAddress = null;
		BigInteger amount = BigInteger.ONE;
		try {
			
			TransactionReceipt receipt = contract.newGreeting(new Utf8String("adopapa 12345678900000")).send();
			System.out.println(receipt.getBlockNumber());
			
			Utf8String greeting = contract.greet().send();
			System.out.println(greeting.getValue());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
